package tcp

import (
	"errors"
	"fmt"
	"io"
	"npxy-go/pkg/util/converter"
)

// Buffer to improve system performance and reduce memory allocates.
//it would allocate extra bytes at the start of `data` that is use to
//hold your protocol heads.
type Buffer struct {
	data []byte
	zero int
	off  int
}

func NewBuffer() *Buffer {
	return NewBufferWithHead(0, 0)
}

func NewBufferWithHead(headSize, bodySize int) *Buffer {
	return &Buffer{
		data: make([]byte, headSize, headSize+bodySize),
		zero: headSize,
		off:  headSize,
	}
}

func (b *Buffer) Reset() {
	b.off = b.zero
	b.data = b.data[:b.zero]
}

func (b *Buffer) ResetZero(zero int) {
	b.zero = zero
	b.Reset()
}

func (b *Buffer) Frame() []byte {
	return b.data[b.off:]
}

func (b *Buffer) HeadLen() int {
	return b.zero - b.off
}

func (b *Buffer) BodyLen() int {
	return len(b.data) - b.zero
}

func (b *Buffer) FrameLen() int {
	return len(b.data) - b.off
}

func (b *Buffer) Cap() int {
	return cap(b.data)
}

func (b *Buffer) BodyCap() int {
	return b.Cap() - b.zero
}

func (b *Buffer) AddBytes(bs []byte) bool {
	l := len(bs)

	if b.off-l < 0 {
		b.expandHead(l)
	}

	b.off -= l
	copy(b.data[b.off:], bs)
	return true
}

func (b *Buffer) AddInt8(i int8) bool {
	return b.AddBytes(converter.Int8ToBytes(i))
}

func (b *Buffer) AddInt16(i int16) bool {
	return b.AddBytes(converter.Int16ToBytes(i))
}

func (b *Buffer) AddInt32(i int32) bool {
	return b.AddBytes(converter.Int32ToBytes(i))
}

func (b *Buffer) AddUInt8(i uint8) bool {
	return b.AddBytes(converter.UInt8ToBytes(i))
}

func (b *Buffer) AddUInt16(i uint16) bool {
	return b.AddBytes(converter.UInt16ToBytes(i))
}

func (b *Buffer) AddUInt32(i uint32) bool {
	return b.AddBytes(converter.UInt32ToBytes(i))
}

func (b *Buffer) ReadBytes(l int) []byte {
	nOff := b.off + l
	if nOff > len(b.data) {
		return b.data[b.off:]
	}
	res := b.data[b.off:nOff]
	b.off = nOff
	return res
}

func (b *Buffer) ReadAll() []byte {
	return b.ReadBytes(b.FrameLen())
}

func (b *Buffer) ReadInt8() int8 {
	return converter.BytesToInt8(b.ReadBytes(1))
}

func (b *Buffer) ReadInt16() int16 {
	return converter.BytesToInt16(b.ReadBytes(2))
}

func (b *Buffer) ReadInt32() int32 {
	return converter.BytesToInt32(b.ReadBytes(4))
}

func (b *Buffer) ReadInt64() int64 {
	return converter.BytesToInt64(b.ReadBytes(8))
}

func (b *Buffer) ReadUInt8() uint8 {
	return converter.BytesToUInt8(b.ReadBytes(1))
}

func (b *Buffer) ReadUInt16() uint16 {
	return converter.BytesToUInt16(b.ReadBytes(2))
}

func (b *Buffer) ReadUInt32() uint32 {
	return converter.BytesToUInt32(b.ReadBytes(4))
}

func (b *Buffer) ReadUInt64() uint64 {
	return converter.BytesToUInt64(b.ReadBytes(8))
}

func (b *Buffer) AppendBytes(bs []byte) {
	b.data = append(b.data, bs...)
}

func (b *Buffer) AppendInt8(i int8) {
	b.AppendBytes(converter.Int8ToBytes(i))
}

func (b *Buffer) AppendInt16(i int16) {
	b.AppendBytes(converter.Int16ToBytes(i))
}

func (b *Buffer) AppendInt32(i int32) {
	b.AppendBytes(converter.Int32ToBytes(i))
}

func (b *Buffer) AppendUInt8(i uint8) {
	b.AppendBytes(converter.UInt8ToBytes(i))
}

func (b *Buffer) AppendUInt16(i uint16) {
	b.AppendBytes(converter.UInt16ToBytes(i))
}

func (b *Buffer) AppendUInt32(i uint32) {
	b.AppendBytes(converter.UInt32ToBytes(i))
}
func (b *Buffer) Read(r io.Reader) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(fmt.Sprint(e))
		}
	}()

	if r == nil {
		return fmt.Errorf("reader closed")
	}

	count, err := r.Read(b.data[b.off:cap(b.data)])

	if err != nil {
		return err
	}

	b.data = b.data[:b.off+count]

	return nil
}

//Split unsafe
func (b *Buffer) Split(length int) (b1, b2 *Buffer) {
	boundary := b.off + length

	b1 = &Buffer{
		data: b.data[:boundary],
		off:  b.off,
		zero: b.off,
	}

	b2 = &Buffer{
		data: b.data[:len(b.data)],
		off:  boundary,
		zero: boundary,
	}

	return
}

func (b *Buffer) expandHead(n int) {
	s := (n>>4 + 1) << 4
	l := len(b.data)
	c := cap(b.data)
	if l+s > c {
		d := make([]byte, l+s)
		copy(d[b.off+s:], b.data[b.off:])
		b.data = d
	} else {
		d := b.data[:l+s]
		i := l - 1

		for i >= b.off {
			d[i+s] = b.data[i]
			i--
		}
		b.data = d
	}

	b.off += s
	b.zero += s
}
